/*
 * uHAL OmniVision OV2640 camera driver
 *
 * Copyright (C) 2016, Marek Koza, qyx@krtko.org
 *
 * This file is part of uMesh node firmware (http://qyx.krtko.org/projects/umesh)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

/**
 * The following part is borrowed from the linux ov2640 driver. Some of the
 * registers are named but still there are many without any documentation.
 * Another possible source is the ArduCam project but it is not documented
 * and there are long arrays of magic values only.
 */

/** @todo relicense to gplv3 or bsd */

/*
 * Copyright (C) 2010 Alberto Panizzo <maramaopercheseimorto@gmail.com>
 *
 * Based on ov772x, ov9640 drivers and previous non merged implementations.
 *
 * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 * Copyright (C) 2006, OmniVision
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/types.h>

#define VAL_SET(x, mask, rshift, lshift) ((((x) >> rshift) & mask) << lshift)

/*
 * DSP registers
 * register offset for BANK_SEL == BANK_SEL_DSP
 */
#define R_BYPASS 0x05           /* Bypass DSP */
#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
#define R_BYPASS_USE_DSP 0x00   /* Use the internal DSP */
#define QS 0x44                 /* Quantization Scale Factor */
#define CTRLI 0x50
#define CTRLI_LP_DP 0x80
#define CTRLI_ROUND 0x40
#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define XOFFL 0x53 /* OFFSET_X[7:0] */
#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
#define YOFFL 0x54 /* OFFSET_Y[7:0] */
#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
#define VHYX 0x55 /* Offset and size completion */
#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8 + 2), 7)
#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8 + 2), 3)
#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
#define DPRP 0x56
#define TEST 0x57 /* Horizontal size completion */
#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9 + 2), 7)
#define ZMOW 0x5A /* Zoom: Out Width  OUTW[7:0] (real/4) */
#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define ZMHH 0x5C /* Zoom: Speed and H&W completion */
#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8 + 2), 2)
#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8 + 2), 0)
#define BPADDR 0x7C /* SDE Indirect Register Access: Address */
#define BPDATA 0x7D /* SDE Indirect Register Access: Data */
#define CTRL2 0x86  /* DSP Module enable 2 */
#define CTRL2_DCW_EN 0x20
#define CTRL2_SDE_EN 0x10
#define CTRL2_UV_ADJ_EN 0x08
#define CTRL2_UV_AVG_EN 0x04
#define CTRL2_CMX_EN 0x01
#define CTRL3 0x87 /* DSP Module enable 3 */
#define CTRL3_BPC_EN 0x80
#define CTRL3_WPC_EN 0x40
#define SIZEL 0x8C /* Image Size Completion */
#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
#define CTRL0 0xC2 /* DSP Module enable 0 */
#define CTRL0_AEC_EN 0x80
#define CTRL0_AEC_SEL 0x40
#define CTRL0_STAT_SEL 0x20
#define CTRL0_VFIRST 0x10
#define CTRL0_YUV422 0x08
#define CTRL0_YUV_EN 0x04
#define CTRL0_RGB_EN 0x02
#define CTRL0_RAW_EN 0x01
#define CTRL1 0xC3 /* DSP Module enable 1 */
#define CTRL1_CIP 0x80
#define CTRL1_DMY 0x40
#define CTRL1_RAW_GMA 0x20
#define CTRL1_DG 0x10
#define CTRL1_AWB 0x08
#define CTRL1_AWB_GAIN 0x04
#define CTRL1_LENC 0x02
#define CTRL1_PRE 0x01
#define R_DVP_SP 0xD3 /* DVP output speed control */
#define R_DVP_SP_AUTO_MODE 0x80
#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); \
                                *          = sysclk (48)/(2*[6:0]) (RAW);*/
#define IMAGE_MODE 0xDA        /* Image Output Format Select */
#define IMAGE_MODE_Y8_DVP_EN 0x40
#define IMAGE_MODE_JPEG_EN 0x10
#define IMAGE_MODE_YUV422 0x00
#define IMAGE_MODE_RAW10 0x04 /* (DVP) */
#define IMAGE_MODE_RGB565 0x08
#define IMAGE_MODE_HREF_VSYNC 0x02  /* HREF timing select in DVP JPEG output \
                                     * mode (0 for HREF is same as sensor) */
#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP             \
                                     *    1: Low byte first UYVY (C2[4] =0) \
                                     *        VYUY (C2[4] =1)               \
                                     *    0: High byte first YUYV (C2[4]=0) \
                                     *        YVYU (C2[4] = 1) */
#define RESET 0xE0                  /* Reset */
#define RESET_MICROC 0x40
#define RESET_SCCB 0x20
#define RESET_JPEG 0x10
#define RESET_DVP 0x04
#define RESET_IPU 0x02
#define RESET_CIF 0x01
#define REGED 0xED /* Register ED */
#define REGED_CLK_OUT_DIS 0x10
#define MS_SP 0xF0   /* SCCB Master Speed */
#define SS_ID 0xF7   /* SCCB Slave ID */
#define SS_CTRL 0xF8 /* SCCB Slave Control */
#define SS_CTRL_ADD_AUTO_INC 0x20
#define SS_CTRL_EN 0x08
#define SS_CTRL_DELAY_CLK 0x04
#define SS_CTRL_ACC_EN 0x02
#define SS_CTRL_SEN_PASS_THR 0x01
#define MC_BIST 0xF9       /* Microcontroller misc register */
#define MC_BIST_RESET 0x80 /* Microcontroller Reset */
#define MC_BIST_BOOT_ROM_SEL 0x40
#define MC_BIST_12KB_SEL 0x20
#define MC_BIST_12KB_MASK 0x30
#define MC_BIST_512KB_SEL 0x08
#define MC_BIST_512KB_MASK 0x0C
#define MC_BIST_BUSY_BIT_R 0x02
#define MC_BIST_MC_RES_ONE_SH_W 0x02
#define MC_BIST_LAUNCH 0x01
#define BANK_SEL 0xFF /* Register Bank Select */
#define BANK_SEL_DSP 0x00
#define BANK_SEL_SENS 0x01

/*
 * Sensor registers
 * register offset for BANK_SEL == BANK_SEL_SENS
 */
#define GAIN 0x00 /* AGC - Gain control gain setting */
#define COM1 0x03 /* Common control 1 */
#define COM1_1_DUMMY_FR 0x40
#define COM1_3_DUMMY_FR 0x80
#define COM1_7_DUMMY_FR 0xC0
#define COM1_VWIN_LSB_UXGA 0x0F
#define COM1_VWIN_LSB_SVGA 0x0A
#define COM1_VWIN_LSB_CIF 0x06
#define REG04 0x04           /* Register 04 */
#define REG04_DEF 0x20       /* Always set */
#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
#define REG04_VREF_EN 0x10
#define REG04_HREF_EN 0x08
#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
#define REG08 0x08                           /* Frame Exposure One-pin Control Pre-charge Row Num */
#define COM2 0x09                            /* Common control 2 */
#define COM2_SOFT_SLEEP_MODE 0x10            /* Soft sleep mode */
                                             /* Output drive capability */
#define COM2_OCAP_Nx_SET(N) (((N)-1) & 0x03) /* N = [1x .. 4x] */
#define PID 0x0A                             /* Product ID Number MSB */
#define VER 0x0B                             /* Product ID Number LSB */
#define COM3 0x0C                            /* Common control 3 */
#define COM3_BAND_50H 0x04                   /* 0 For Banding at 60H */
#define COM3_BAND_AUTO 0x02                  /* Auto Banding */
#define COM3_SING_FR_SNAPSH 0x01             /* 0 For enable live video output after the \
                                              * snapshot sequence*/
#define AEC 0x10                             /* AEC[9:2] Exposure Value */
#define CLKRC 0x11                           /* Internal clock */
#define CLKRC_EN 0x80
#define CLKRC_DIV_SET(x) (((x)-1) & 0x1F) /* CLK = XVCLK/(x) */
#define COM7 0x12                         /* Common control 7 */
#define COM7_SRST 0x80                    /* Initiates system reset. All registers are \
                                           * set to factory default values after which \
                                           * the chip resumes normal operation */
#define COM7_RES_UXGA 0x00                /* Resolution selectors for UXGA */
#define COM7_RES_SVGA 0x40                /* SVGA */
#define COM7_RES_CIF 0x20                 /* CIF */
#define COM7_ZOOM_EN 0x04                 /* Enable Zoom mode */
#define COM7_COLOR_BAR_TEST 0x02          /* Enable Color Bar Test Pattern */
#define COM8 0x13                         /* Common control 8 */
#define COM8_DEF 0xC0                     /* Banding filter ON/OFF */
#define COM8_BNDF_EN 0x20                 /* Banding filter ON/OFF */
#define COM8_AGC_EN 0x04                  /* AGC Auto/Manual control selection */
#define COM8_AEC_EN 0x01                  /* Auto/Manual Exposure control */
#define COM9 0x14                         /* Common control 9 \
                                      * Automatic gain ceiling - maximum AGC value [7:5]*/
#define COM9_AGC_GAIN_2x 0x00             /* 000 :   2x */
#define COM9_AGC_GAIN_4x 0x20             /* 001 :   4x */
#define COM9_AGC_GAIN_8x 0x40             /* 010 :   8x */
#define COM9_AGC_GAIN_16x 0x60            /* 011 :  16x */
#define COM9_AGC_GAIN_32x 0x80            /* 100 :  32x */
#define COM9_AGC_GAIN_64x 0xA0            /* 101 :  64x */
#define COM9_AGC_GAIN_128x 0xC0           /* 110 : 128x */
#define COM10 0x15                        /* Common control 10 */
#define COM10_PCLK_HREF 0x20              /* PCLK output qualified by HREF */
#define COM10_PCLK_RISE 0x10            /* Data is updated at the rising edge of \
                                         * PCLK (user can latch data at the next \
                                         * falling edge of PCLK).                \
                                         * 0 otherwise. */
#define COM10_HREF_INV 0x08             /* Invert HREF polarity: \
                                         * HREF negative for valid data*/
#define COM10_VSINC_INV 0x02            /* Invert VSYNC polarity */
#define HSTART 0x17                     /* Horizontal Window start MSB 8 bit */
#define HEND 0x18                       /* Horizontal Window end MSB 8 bit */
#define VSTART 0x19                     /* Vertical Window start MSB 8 bit */
#define VEND 0x1A                       /* Vertical Window end MSB 8 bit */
#define MIDH 0x1C                       /* Manufacturer ID byte - high */
#define MIDL 0x1D            /* Manufacturer ID byte - low  */
#define AEW 0x24            /* AGC/AEC - Stable operating region (upper limit) */
#define AEB 0x25            /* AGC/AEC - Stable operating region (lower limit) */
#define VV 0x26             /* AGC/AEC Fast mode operating region */
#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
#define REG2A 0x2A            /* Dummy pixel insert MSB */
#define FRARL 0x2B            /* Dummy pixel insert LSB */
#define ADDVFL 0x2D           /* LSB of insert dummy lines in Vertical direction */
#define ADDVFH 0x2E           /* MSB of insert dummy lines in Vertical direction */
#define YAVG 0x2F             /* Y/G Channel Average value */
#define REG32 0x32            /* Common Control 32 */
#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
#define ARCOM2 0x34           /* Zoom: Horizontal start point */
#define REG45 0x45            /* Register 45 */
#define FLL 0x46              /* Frame Length Adjustment LSBs */
#define FLH 0x47              /* Frame Length Adjustment MSBs */
#define COM19 0x48            /* Zoom: Vertical start point */
#define ZOOMS 0x49            /* Zoom: Vertical start point */
#define COM22 0x4B            /* Flash light control */
#define COM25 0x4E            /* For Banding operations */
#define BD50 0x4F             /* 50Hz Banding AEC 8 LSBs */
#define BD60 0x50             /* 60Hz Banding AEC 8 LSBs */
#define REG5D 0x5D            /* AVGsel[7:0],   16-zone average weight option */
#define REG5E 0x5E            /* AVGsel[15:8],  16-zone average weight option */
#define REG5F 0x5F            /* AVGsel[23:16], 16-zone average weight option */
#define REG60 0x60            /* AVGsel[31:24], 16-zone average weight option */
#define HISTO_LOW 0x61        /* Histogram Algorithm Low Level */
#define HISTO_HIGH 0x62       /* Histogram Algorithm High Level */

#define MANUFACTURER_ID 0x7FA2
#define PID_OV2640 0x2642
#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))

struct regval_list
{
    uint8_t reg_num;
    uint8_t value;
};

/* Supported resolutions */
enum ov2640_width
{
    W_QCIF = 176,
    W_QVGA = 320,
    W_CIF = 352,
    W_VGA = 640,
    W_SVGA = 800,
    W_XGA = 1024,
    W_SXGA = 1280,
    W_UXGA = 1600,
};

enum ov2640_height
{
    H_QCIF = 144,
    H_QVGA = 240,
    H_CIF = 288,
    H_VGA = 480,
    H_SVGA = 600,
    H_XGA = 768,
    H_SXGA = 1024,
    H_UXGA = 1200,
};

struct ov2640_win_size
{
    char *name;
    enum ov2640_width width;
    enum ov2640_height height;
    const struct regval_list *regs;
};

#define ENDMARKER  \
    {              \
        0xff, 0xff \
    }
#define OV2640_SIZE(n, w, h, r)                       \
    {                                                 \
        .name = n, .width = w, .height = h, .regs = r \
    }

extern const struct regval_list ov2640_init_regs[];
extern const struct regval_list ov2640_size_change_preamble_regs[];
extern const struct regval_list ov2640_qcif_regs[];
extern const struct regval_list ov2640_qvga_regs[];
extern const struct regval_list ov2640_cif_regs[];
extern const struct regval_list ov2640_vga_regs[];
extern const struct regval_list ov2640_lcd_regs[];
extern const struct regval_list ov2640_svga_regs[];
extern const struct regval_list ov2640_xga_regs[];
extern const struct regval_list ov2640_sxga_regs[];
extern const struct regval_list ov2640_uxga_regs[];
extern const struct ov2640_win_size ov2640_supported_win_sizes[];
extern const struct regval_list ov2640_format_change_preamble_regs[];
extern const struct regval_list ov2640_yuyv_regs[];
extern const struct regval_list ov2640_uyvy_regs[];
extern const struct regval_list ov2640_rgb565_be_regs[];
extern const struct regval_list ov2640_rgb565_le_regs[];
extern const struct regval_list ov2640_jpeg_regs[];
extern const struct regval_list ov2640_light_mode_sunny_regs[];
extern const struct ov2640_win_size ov2640_supported_win_sizes[8];